package com.design.proxy.dynamicproxy.basic;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**  https://blog.csdn.net/jupiter_888/article/details/103852693
 * 在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口，
 * 通过这个类和这个接口可以生成  JDK动态代理类 和  动态代理对象。
 * 创建一个动态代理对象步骤，具体代码见后面：
 *  创建一个InvocationHandler对象
 *      //创建一个与代理对象相关联的InvocationHandler
 *      InvocationHandler stuHandler = new MyInvocationHandler<Person>(stu);
 *  使用Proxy类的getProxyClass静态方法生成一个 动态代理类 stuProxyClass
 *      Class<?> stuProxyClass = Proxy.getProxyClass(Person.class.getClassLoader(), new Class<?>[] {Person.class});
 *  获得 stuProxyClass 中一个带InvocationHandler参数的构造器constructor
 *      Constructor<?> constructor = PersonProxy.getConstructor(InvocationHandler.class);
 *  通过构造器constructor来创建一个动态实例stuProxy
 *      Person stuProxy = (Person) constructor.newInstance(stuHandler);
 *
 * 就此，一个动态代理对象就创建完毕，当然，上面四个步骤可以通过Proxy类的newProxyInstances方法来简化：
 * //创建一个与代理对象相关联的InvocationHandler
 *   InvocationHandler stuHandler = new MyInvocationHandler<Person>(stu);
 * //创建一个代理对象stuProxy，代理对象的每个执行方法都会替换执行Invocation中的invoke方法
 *   Person stuProxy= (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler);
 *
 *  public class ProxyTest {
 *   public static void main(String[] args) {
 *     Person zhangsan = new Student("张三"); //创建一个实例对象，这个对象是被代理的对象
 *     InvocationHandler stuHandler = new StuInvocationHandler<Person>(zhangsan); //创建一个与代理对象相关联的InvocationHandler
 *     //创建一个代理对象stuProxy来代理zhangsan，代理对象的每个执行方法都会替换执行Invocation中的invoke方法
 *     Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler)；
 *     stuProxy.giveMoney();  //代理执行上交班费的方法
 *  }
 * }
 * 我们执行这个ProxyTest类，先想一下，我们创建了一个需要被代理的学生张三，将zhangsan对象传给了stuHandler中，我们在创建代理对象
 * stuProxy时，将stuHandler作为参数了的，上面也有说到所有执行代理对象的方法都会被替换成执行invoke方法，也就是说，最后执行的是
 * StuInvocationHandler中的invoke方法。所以在看到下面的运行结果也就理所当然了。
 *
 * JDK动态代理(基于implements) 与 CGLIB动态代理(基于extends)：
 * jdk创建对象的速度远大于cglib，这是由于cglib创建对象时需要操作字节码。cglib执行速度略大于jdk，所以比较适合单例模式。另外由于CGLIB的
 * 大部分类是直接对Java字节码进行操作，这样生成的类会在Java的永久堆中。如果动态代理操作过多，容易造成永久堆满，触发OutOfMemory异常。
 * spring默认使用jdk动态代理，如果类没有接口，则使用cglib。
 *
 *
 */
public class PayProxy {

    private Object target_service;

    public PayProxy(Object target_service) {
        this.target_service = target_service;
    }

    public Object getPayProxy() {
        //构造参数
        ClassLoader classLoader = target_service.getClass().getClassLoader();   //2
        Class[] interfaces = target_service.getClass().getInterfaces();         //3获取到接口
        InvocationHandler invocationHandler = new InvocationHandler() {         //4  调度器
            @Override //只有调用了接口的 具体方法才会走入这里（实现类impl: proxy, 调用的impl里面的某个方法名: method，该方法的入参: args）
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("start...");
                Object result = method.invoke(target_service, args);//5 实现类impl: target, 参数: args
                System.out.println("stop...");
                return result;
            }
        };
        //装入参数                                     2            3            4
        Object proxy = Proxy.newProxyInstance(classLoader, interfaces, invocationHandler); //
        return proxy;
    }

}
